﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using libgambattebridge;
using System.IO;
using WeifenLuo.WinFormsUI.Docking;
using RibbonLib.Controls;
using System.Runtime.InteropServices;

// - profiler (pc hits per vblank)
//      - parse .map files
//      - store PC hits per vblank
//      - create visual output of result
// - debugger (breakpoints & ram view & registers)
//      - support breakpoints
//      - support memory breakpoints
//      - printf to tty
//      - create memory view
//      - create vram view
//
// - polish
//		- custom executable name
//		- open/restore editor windows
//      - remove files
//
// DONE:
// - editor
//      - link project
//      - support compile & run
//      - compile project
//          - store project file
//      - multiple file support
//      - working project editor
//      - add c & header files
//      - saving of files

namespace WindowsFormsApplication1
{
    public enum RibbonMarkupCommands : uint
    {
        cmdButtonRunDebugger = 1005,
        cmdButtonPauseDebugger = 1006,
        cmdButtonNewFile = 1008,
    }

    public partial class Form1 : Form, IMessageFilter
    {
        GbRenderer gb;
        Editor ed;
        Errors ou;
        ProjectExplorer pe;
        Builder bu;
        Project pr;
        NewFile nf;

        Dictionary<string, Editor> editors;

        private RibbonButton buttonPauseDbg;
        private RibbonButton buttonRunDbg;
        private RibbonButton buttonNewFile;

        public Form1()
        {
            InitializeComponent();

            editors = new Dictionary<string, Editor>();
            gb = new GbRenderer();
            ed = new Editor();
            ou = new Errors();
            ou.GotoError += new Errors.GotoErrorHandler(ou_GotoError);
            pr = new Project();
            bu = new Builder(pr, this);
            bu.LinkerDone += new Builder.LinkerDoneHandler(bu_LinkerDone);
            pe = new ProjectExplorer();
            nf = new NewFile(pr);
            pr.ProjectFileAdded += new Project.ProjectFileAddedHandler(pr_ProjectFileAdded);
            pr.Load("../../Project.xml");
            //pr.Save("../../Project.xml");
            pe.OpenFile += new ProjectExplorer.OpenFileHandler(pe_OpenFile);
            bu.CompilerError += new Builder.CompilerErrorHandler(bu_CompilerError);
            buttonRunDbg = new RibbonButton(ribbon1, (uint)RibbonMarkupCommands.cmdButtonRunDebugger);
            buttonRunDbg.ExecuteEvent += new EventHandler<RibbonLib.Controls.Events.ExecuteEventArgs>(buttonRunDbg_ExecuteEvent);
            buttonPauseDbg = new RibbonButton(ribbon1, (uint)RibbonMarkupCommands.cmdButtonPauseDebugger);
            buttonPauseDbg.ExecuteEvent += new EventHandler<RibbonLib.Controls.Events.ExecuteEventArgs>(buttonPauseDbg_ExecuteEvent);
            buttonNewFile = new RibbonButton(ribbon1, (uint)RibbonMarkupCommands.cmdButtonNewFile);
            buttonNewFile.ExecuteEvent += new EventHandler<RibbonLib.Controls.Events.ExecuteEventArgs>(buttonNewFile_ExecuteEvent);

            Application.AddMessageFilter(this);
            this.FormClosed += (o, e) => Application.RemoveMessageFilter(this);
            this.FormClosing += (o, e) => { SaveAll(); pr.Save("../../Project.xml"); };
        }

        void buttonPauseDbg_ExecuteEvent(object sender, RibbonLib.Controls.Events.ExecuteEventArgs e)
        {
            gb.Breakpoint();
        }

        void bu_LinkerDone(object sender, EventArgs e)
        {
            PlayGame(System.IO.Path.GetFullPath("game.gb"));
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern short GetKeyState(int keyCode);

        public bool PreFilterMessage(ref Message m)
        {
            // F5 = build key
            if (m.Msg == 0x100)
            {
                if ((System.Windows.Forms.Keys)m.WParam == System.Windows.Forms.Keys.F5)
                {
                    Build();
                    return true;
                }

                if ((System.Windows.Forms.Keys)m.WParam == System.Windows.Forms.Keys.S)
                {
                    if (GetKeyState(0x11) != 0)
                    {
                        SaveAll();
                        return true;
                    }
                }
            }
            return false;
        }

        void buttonNewFile_ExecuteEvent(object sender, RibbonLib.Controls.Events.ExecuteEventArgs e)
        {
            DialogResult r = nf.ShowDialog(this);
            if (r == System.Windows.Forms.DialogResult.OK)
                pr.AddNewFile(nf.Filename);
        }

        void pr_ProjectFileAdded(object sender, ProjectFileAddedArgs e)
        {
            pe.AddFile(e.ProjectFile);
        }

        void ou_GotoError(object sender, GotoErrorArgs e)
        {
            OpenOrFindCachedEditor(e.Filename)
                .GotoLine(e.Line > 0 ? e.Line - 1 : 0);
        }

        void pe_OpenFile(object sender, OpenFileArgs e)
        {
            OpenOrFindCachedEditor(e.file.Path);
        }

        Editor OpenOrFindCachedEditor(string path)
        {
            if (editors.ContainsKey(path))
            {
                editors[path].Activate();
                return editors[path];
            }
            else
            {
                var edit = new Editor();
                edit.FormClosing += new FormClosingEventHandler(edit_FormClosing);
                edit.LoadFile(path);
                edit.Show(dockPanel1);
                editors[path] = edit;
                return edit;
            }
        }

        void edit_FormClosing(object sender, FormClosingEventArgs e)
        {
            Editor edit = sender as Editor;
            editors.Remove(edit.Filename);
        }

        void bu_CompilerError(object sender, CompilerErrorArg e)
        {
            ou.AddError(e.Data);
        }

        void buttonRunDbg_ExecuteEvent(object sender, RibbonLib.Controls.Events.ExecuteEventArgs e)
        {
            Build();
        }

        private void Build()
        {
            ou.Clear();
            //List<string> files = new List<string>();
            //for(int i = 0; i < 10; i++)
            //    files.Add(@"C:\Users\Jasper\Documents\gb\gbdk\examples\gb\man.c");
            bu.Build();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            WindowState = FormWindowState.Maximized;
            PositionElements();

            if (File.Exists("docklayout.xml"))
            {
                dockPanel1.LoadFromXml("docklayout.xml", new DeserializeDockContent(GetContentFromPersistString));
            }

            //timer1.Enabled = false;
            gb.Show(dockPanel1);
            ed.Show(dockPanel1);
            ou.Show(dockPanel1);
            pe.Show(dockPanel1);
        }

        private IDockContent GetContentFromPersistString(string persistString)
        {
            if (persistString == typeof(GbRenderer).ToString()) return gb;
            //if (persistString == typeof(Editor).ToString()) return ed;
            if (persistString == typeof(Errors).ToString()) return ou;
            if (persistString == typeof(ProjectExplorer).ToString()) return pe;
            return null;
        }

        private void LoadGame()
        {
            OpenFileDialog d = new OpenFileDialog();
            d.Filter = "Gameboy file (.gb)|*.gb";
            d.InitialDirectory = @"C:\Users\Jasper\Documents\gb\gbdk\examples\gb\";

            if (d.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                PlayGame(d.FileName);
            }
        }

        private void PlayGame(string filename)
        {
            gb.LoadFile(filename);
            timer1.Enabled = true;
            timer1.Interval = 10;
            timer1.Tick += (o, e) => gb.Tick();
            timer1.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            
        }

        private void pictureBox2_Paint(object sender, PaintEventArgs e)
        {

        }

        private void ribbonButton1_Click(object sender, EventArgs e)
        {
            LoadGame();
        }

        private void openToolStripMenuItem_Click_1(object sender, EventArgs e)
        {
            LoadGame();
        }

        private void dockPanel1_ActiveContentChanged(object sender, EventArgs e)
        {

        }
        string filename;

        private void compileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //ou.CompileFile(filename);
        }

        private void openFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog d = new OpenFileDialog();
            d.InitialDirectory = @"C:\Users\Jasper\Documents\gb\gbdk\examples\gb\";
            d.Filter = "C Files (.c)|*.c";

            if (d.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                filename = d.FileName;
                ed.LoadFile(d.FileName);
            }
        }

        private void SaveAll()
        {
            foreach (var e in editors)
            {
                if (e.Value.NeedsSaving)
                {
                    e.Value.SaveFile();
                }
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            dockPanel1.SaveAsXml("docklayout.xml");
        }

        private void ribbon1_Click(object sender, EventArgs e)
        {

        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            PositionElements();
        }

        private void PositionElements()
        {
            dockPanel1.Top = ribbon1.Height;
            dockPanel1.Left = 0;
            dockPanel1.Width = ClientSize.Width;
            dockPanel1.Height = ClientSize.Height - ribbon1.Height;
        }
    }
}
